package com.terra.ns.imp.common.web.exception

import cn.dev33.satoken.exception.NotLoginException
import cn.dev33.satoken.exception.NotPermissionException
import cn.dev33.satoken.exception.NotRoleException
import cn.dev33.satoken.exception.SameTokenInvalidException
import com.terra.ns.imp.common.core.constant.GlobalErrorCode.INTERNAL_SERVER_ERROR
import com.terra.ns.imp.common.core.constant.GlobalErrorCode.METHOD_NOT_ALLOWED
import com.terra.ns.imp.common.core.constant.GlobalErrorCode.REQUEST_NOT_FOUND
import com.terra.ns.imp.common.core.constant.GlobalErrorCode.REQUEST_PARAM_ERROR
import com.terra.ns.imp.common.core.constant.GlobalErrorCode.USER_NO_LOGIN
import com.terra.ns.imp.common.core.constant.GlobalErrorCode.USER_NO_PERM
import com.terra.ns.imp.common.core.exception.BaseException
import com.terra.ns.imp.common.core.exception.ServiceException
import com.terra.ns.imp.common.vo.response.ResultBean
import jakarta.servlet.http.HttpServletRequest
import jakarta.validation.ConstraintViolationException
import org.slf4j.LoggerFactory
import org.springframework.http.converter.HttpMessageNotReadableException
import org.springframework.validation.BindException
import org.springframework.web.HttpRequestMethodNotSupportedException
import org.springframework.web.bind.MethodArgumentNotValidException
import org.springframework.web.bind.MissingServletRequestParameterException
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.RestControllerAdvice
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException
import org.springframework.web.servlet.NoHandlerFoundException

/**
@author qins
@date 2023/6/4
@desc 全局异常处理器
 */
@RestControllerAdvice
class GlobalExceptionHandler {

    private val log = LoggerFactory.getLogger(this.javaClass)

    /**
     * 处理业务异常 ServiceException
     */
    @ExceptionHandler(ServiceException::class)
    fun serviceExceptionHandler(ex: ServiceException): ResultBean<*> {
        log.error("[GlobalExceptionHandler serviceExceptionHandler]", ex)
        return ResultBean.fail<Any>(ex.errorCode, ex.shortMsg)
    }

    /**
     * 处理业务异常 ServiceException
     */
    @ExceptionHandler(BaseException::class)
    fun baseExceptionHandler(ex: BaseException): ResultBean<*> {
        log.error("[GlobalExceptionHandler baseExceptionHandler]", ex)
        return ResultBean.fail<Any>(ex.code, ex.message ?: "")
    }

    /**
     * 处理 SpringMVC 请求参数缺失
     * 例如说，接口上设置了 @RequestParam("xx") 参数，结果并未传递 xx 参数
     */
    @ExceptionHandler(MissingServletRequestParameterException::class)
    fun missingServletRequestParameterExceptionHandler(ex: MissingServletRequestParameterException): ResultBean<*> {
        log.error("[GlobalExceptionHandler missingServletRequestParameterExceptionHandler]", ex)
        return ResultBean.fail<Any>(REQUEST_PARAM_ERROR, "缺失参数：${ex.parameterName}")
    }

    /**
     * 缺失请求参数
     */
    @ExceptionHandler(HttpMessageNotReadableException::class)
    fun httpMessageNotReadableExceptionHandler(ex: HttpMessageNotReadableException): ResultBean<*> {
        log.error("[GlobalExceptionHandler httpMessageNotReadableExceptionHandler]", ex)
        return ResultBean.fail<Any>(REQUEST_PARAM_ERROR, "")
    }


    /**
     * 处理 SpringMVC 请求参数类型错误
     * 例如说，接口上设置了 @RequestParam("xx") 参数为 Integer，结果传递 xx 参数类型为 String
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException::class)
    fun methodArgumentTypeMismatchExceptionHandler(ex: MethodArgumentTypeMismatchException): ResultBean<*> {
        log.error("[GlobalExceptionHandler missingServletRequestParameterExceptionHandler]", ex)
        return ResultBean.fail<Any>(REQUEST_PARAM_ERROR, "请求参数类型错误:${ex.message}")
    }

    /**
     * 处理 SpringMVC 参数校验不正确
     */
    @ExceptionHandler(MethodArgumentNotValidException::class)
    fun methodArgumentNotValidExceptionExceptionHandler(ex: MethodArgumentNotValidException): ResultBean<*> {
        log.error("[GlobalExceptionHandler methodArgumentNotValidExceptionExceptionHandler]", ex)
        return ResultBean.fail<Any>(REQUEST_PARAM_ERROR, "${ex.bindingResult.fieldError?.defaultMessage}")
    }

    /**
     * 处理 SpringMVC 参数绑定不正确，本质上也是通过 Validator 校验
     */
    @ExceptionHandler(BindException::class)
    fun bindExceptionHandler(ex: BindException): ResultBean<*> {
        log.error("[GlobalExceptionHandler handleBindException]", ex)
        return ResultBean.fail<Any>(REQUEST_PARAM_ERROR, "${ex.fieldError?.defaultMessage}")
    }

    /**
     * 处理 Validator 校验不通过产生的异常
     */
    @ExceptionHandler(ConstraintViolationException::class)
    fun constraintViolationExceptionHandler(ex: ConstraintViolationException): ResultBean<*> {
        log.error("[GlobalExceptionHandler constraintViolationExceptionHandler]", ex)
        return ResultBean.fail<Any>(REQUEST_PARAM_ERROR, "${ex.constraintViolations.iterator().next().message}")
    }

    /**
     * 处理 SpringMVC 请求地址不存在
     */
    @ExceptionHandler(NoHandlerFoundException::class)
    fun noHandlerFoundExceptionHandler(ex: NoHandlerFoundException): ResultBean<*> {
        log.error("[GlobalExceptionHandler noHandlerFoundExceptionHandler]", ex)
        return ResultBean.fail<Any>(REQUEST_NOT_FOUND, "${ex.requestURL}")
    }

    /**
     * 处理 SpringMVC 请求方法不正确
     * 例如说，A 接口的方法为 GET 方式，结果请求方法为 POST 方式，导致不匹配
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException::class)
    fun httpRequestMethodNotSupportedExceptionHandler(ex: HttpRequestMethodNotSupportedException): ResultBean<*> {
        log.error("[GlobalExceptionHandler httpRequestMethodNotSupportedExceptionHandler]", ex)
        return ResultBean.fail<Any>(METHOD_NOT_ALLOWED, "${ex.message}")
    }


    /**
     * 处理系统异常，兜底处理所有的一切
     */
    @ExceptionHandler(Exception::class)
    fun defaultExceptionHandler(req: HttpServletRequest?, ex: Throwable?): ResultBean<*> {
        log.error("[GlobalExceptionHandler defaultExceptionHandler]", ex)
        return ResultBean.fail<Any>(INTERNAL_SERVER_ERROR)
    }

    /**
     * 权限码异常
     */
    @ExceptionHandler(NotPermissionException::class)
    fun handleNotPermissionException(ex: NotPermissionException): ResultBean<*> {
        log.error("[GlobalExceptionHandler handleNotPermissionException]", ex)
        return ResultBean.fail<Any>(USER_NO_PERM)
    }

    /**
     * 角色权限异常
     */
    @ExceptionHandler(NotRoleException::class)
    fun handleNotRoleException(ex: NotRoleException): ResultBean<*> {
        log.error("[GlobalExceptionHandler handleNotPermissionException]", ex)
        return ResultBean.fail<Any>(USER_NO_PERM)
    }

    /**
     * 认证失败
     */
    @ExceptionHandler(NotLoginException::class)
    fun handleNotLoginException(ex: NotLoginException): ResultBean<*> {
        log.error("[GlobalExceptionHandler handleNotLoginException]", ex)
        return ResultBean.fail<Any>(USER_NO_LOGIN)
    }

    /**
     * token无效认证
     */
    @ExceptionHandler(SameTokenInvalidException::class)
    fun handleSameTokenInvalidException(ex: SameTokenInvalidException): ResultBean<*> {
        log.error("[GlobalExceptionHandler handleSameTokenInvalidException]", ex)
        return ResultBean.fail<Any>(USER_NO_LOGIN)
    }


    /**
     * 处理所有异常，主要是提供给 Filter和Interceptor 调用 走统一返回
     * 因为 Filter 不走 SpringMVC 的流程，但是我们又需要兜底处理异常，所以这里提供一个全量的异常处理过程，保持逻辑统一。
     * @param request 请求
     * @param ex 异常
     * @return 通用返回
     */
    fun allExceptionHandler(request: HttpServletRequest?, ex: Throwable): ResultBean<*> {
        if (ex is ServiceException) {
            return serviceExceptionHandler(ex)
        }
        if (ex is BaseException) {
            return baseExceptionHandler(ex)
        }
        if (ex is MissingServletRequestParameterException) {
            return missingServletRequestParameterExceptionHandler(ex)
        }
        if (ex is MethodArgumentTypeMismatchException) {
            return methodArgumentTypeMismatchExceptionHandler(ex)
        }
        if (ex is MethodArgumentNotValidException) {
            return methodArgumentNotValidExceptionExceptionHandler(ex)
        }
        if (ex is BindException) {
            return bindExceptionHandler(ex)
        }
        if (ex is ConstraintViolationException) {
            return constraintViolationExceptionHandler(ex)
        }

        if (ex is NoHandlerFoundException) {
            return noHandlerFoundExceptionHandler(ex)
        }
        if (ex is HttpRequestMethodNotSupportedException) {
            return httpRequestMethodNotSupportedExceptionHandler(ex)
        }
        if (ex is NotLoginException) {
            return handleNotLoginException(ex)
        }
        if (ex is NotPermissionException) {
            return handleNotPermissionException(ex)
        }
        if (ex is NotRoleException) {
            return handleNotRoleException(ex)
        }
        if (ex is SameTokenInvalidException) {
            return handleSameTokenInvalidException(ex)
        }
        return defaultExceptionHandler(request, ex)
    }


}